package com.googlecode.cswish.view;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.Writer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.components.Component;

import com.googlecode.cswish.Config;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.struts.ContextHolder;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.FrameConstant;
import com.googlecode.cswish.util.FreeMarkerHelper;
import com.googlecode.cswish.util.FreemarkerDStringTplEngine;
import com.googlecode.cswish.util.FreemarkerDStringTplEngine.StringTemplateBuilder;
import com.googlecode.cswish.util.FreemarkerDStringTplEngine.StringTemplateResource;
import com.googlecode.cswish.util.TextUtil;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;

import freemarker.core.Environment;
import freemarker.template.Template;

/**
 * @author Jerry.Feng Date: 2008-6-3
 */
public class Call extends Component {
	
	private static final Log logger = LogFactory.getLog(Call.class);
	
	// static page
	protected boolean staticPage;
	
	protected String name;
	
	protected String action;
	
	protected String tpl;
	
    private ObjectFactory objectFactory;

    @Resource
    private Config config;

    @Resource
    private FreemarkerDStringTplEngine freemarkerDStringTplEngine;

	private final static String BUILDER_NAME = "Call";
    
	public Call(ValueStack stack) {
		super(stack);
	}
	
    @Inject
    public void setObjectFactory(com.opensymphony.xwork2.ObjectFactory objectFactory) {
    	if (this.objectFactory == null) {
	    	this.objectFactory = ObjectFactory.getInstance();
	    	this.objectFactory.autoWireBean(this);
	    	
	    	if (!freemarkerDStringTplEngine.hasBuilder(BUILDER_NAME)) {
	    		freemarkerDStringTplEngine.registerBuilder(BUILDER_NAME, new CallStringTemplateBuilder());
	    	}
    	}
    }
        
    class CallStringTemplateBuilder implements StringTemplateBuilder {

		@Override
		public StringTemplateResource create(String name) {
			String[] keys = getTemplateName(name);
			String action = keys[0];
			String targetElementName = keys[1];
			
			
			// SEARCH LOGIC: inputPath --> code generation path --> special case for 'search'
			String realPath = config.getRealWebRootPath();
			String fullPath = realPath + config.getViewDir() + action;
			
			File file = null;
			String nameContent = null;
			try {
				file = new File(fullPath);
				String content = null;
				if (file.exists()) {
					content = FileUtils.readFileToString(file, "UTF-8");
					nameContent = TextUtil.get(content, targetElementName);
				}
				
				if (nameContent == null) {
					fullPath = config.getRealSitePath() + config.getViewDir4GenCode() + action;
					file = new File(fullPath);
					if (file.exists()) {
						content = FileUtils.readFileToString(file, "UTF-8");
						nameContent = TextUtil.get(content, targetElementName);
					}
					
					if (nameContent == null) {
						fullPath = getFullActionPath(action, realPath);
						file = new File(fullPath);
						if (file.exists()) {
							content = FileUtils.readFileToString(file, "UTF-8");
							nameContent = TextUtil.get(content, targetElementName);
						}
					}
				}
				
				if (nameContent == null) {
					// guess the closest element name
					if (targetElementName.startsWith("name='_gen_execute_")) {		// special logic for the refresh button
						String tryNewName = "<@pageinfo name='_gen_execute_";
						nameContent = TextUtil.get(content, tryNewName);
						if (nameContent != null && logger.isDebugEnabled()) {
							logger.debug("Can't find target node: " + targetElementName + ", try the closest name: _gen_execute_");
						}
					}
					
					if (nameContent == null) {
						logger.debug("Can't find target node: " + targetElementName + ", Use the whole content");
						nameContent = content;
					}
				} else {
					if (logger.isDebugEnabled()) {
						logger.debug("Call path " + fullPath.substring(realPath.length()) + " : " + targetElementName);
					}
				}
			} catch (IOException ex) {
				logger.error("Falied to read file " + fullPath, ex);
			}
			
			String newContent = "<#include '/pages/taglib/core.ftl'>\n<#include '/pages/taglib/common.ftl'>\n<#include '/pages/taglib/container.ftl'>\n" 
						+ nameContent;
			
			StringTemplateResource source = new StringTemplateResource();
			source.setName(name);
			source.setContent(newContent);
			if (file != null) {
				source.setSource(new File[] {file});
				source.setLastCheckTime(file.lastModified());
			}
			return source;
		}
		

		private String getFullActionPath(String action, String realPath) {
			String siteRootPath = config.getRealSitePath();
			String fullPath;
			if (action.endsWith(".search.ftl")) {
				// FIXME: better solution
				// the call tag doesn't support the <#include>
				fullPath = siteRootPath + config.getViewDir4GenCode() 
					+ action.substring(0, action.length() - 11) + "._search.ftl";
				if (!new File(fullPath).exists()) {		// 
					fullPath = siteRootPath + config.getViewDir4GenCode() + action;
				}
			} else {
				fullPath = siteRootPath + config.getViewDir4GenCode() + action;
			}
			return fullPath;
		}
    }
    
    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAction() {
		return action;
	}

	public void setAction(String action) {
		this.action = action;
	}

	public String getTpl() {
		return tpl;
	}

	public void setTpl(String tpl) {
		this.tpl = tpl;
	}

	public boolean isStaticPage() {
		return staticPage;
	}

	public void setStaticPage(boolean staticPage) {
		this.staticPage = staticPage;
	}

	public boolean start(Writer writer) {
		return true;
	}
	
	private String[] getTemplateName(String key) {
		return StringUtils.split(key, '\n');
	}
	
	private String buildTemplateName(String action, String name) {
		return action + '\n' + name;
	}

	public boolean end(Writer writer, String body) {
		if (tpl != null) {
			int lastPos = action.lastIndexOf('.');
			String tplId = StringUtils.split(tpl, ',')[0];
			action = action.substring(0, lastPos + 1) + tplId + action.substring(lastPos);
		}
		
		// TODO: optimize it for the better performance
		// call tag
		if (staticPage) {
			try {
				PageElements ps = (PageElements) getStack().getContext().get(FrameConstant.STACK_PAGEELEMENTS);
				
				String key = buildTemplateName(action, name);
				Template template = freemarkerDStringTplEngine.getTemplate(BUILDER_NAME, key);
				Writer bodyWriter = new CharArrayWriter();
				freemarkerDStringTplEngine.renderTemplate(bodyWriter, getStack(), template);
				
				// output the header & footer
				writer.write(ps.safeContext().buildHeader());
				writer.write(bodyWriter.toString());
				writer.write(ps.safeContext().buildFooter());
			} catch (Exception e) {
				logger.error("error when rendering", e);
	        }
		} else {
			if (action != null) {
				HttpServletRequest request = ServletActionContext.getRequest();
				HttpServletResponse response = ServletActionContext.getResponse();
				try {
					request.getRequestDispatcher(action).include(request, response);
				} catch (Exception ex) {
		            logger.error("can't call action:" + action, ex);
		        }
			}
			
			Object result = Mark.getExtensions(stack).get(name);
			if (result != null) {
				Environment environment = Environment.getCurrentEnvironment();
				FreeMarkerHelper.execute(environment, result, writer);
			} else {
				if (name != null) {
					logger.error("Call failed, can't find module:" + name);
				}
			}
		}
		return false;
	}

}